package services.kingdee

import play.api.{Configuration, Logging}
import play.api.cache.SyncCacheApi
import play.api.libs.json.{JsValue, Json}
import security.Encode.{base642Byte, byte2Base64, hmacSHA256}

import java.nio.charset.Charset
import javax.inject.{Inject, Singleton}
import scala.collection.mutable
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext


case class AuthUser(acctID: String="", userName: String="", password: String="", lcid: Int = 2052)

case class CookieDict(aspSession: String = "000000000000000000000000000000000001", kdSessionid: String = "", messages: JsValue = Json.obj())

@Singleton
class KindeeAuthService @Inject()(
                                   config: Configuration,
                                   cache: SyncCacheApi
                                 )(implicit ec: ExecutionContext)
  extends Logging {
  val sec = security.Encode
  val hmacMassage = "hashlib.sha224((value+str(timestamp)).encode(encoding='UTF-8')).hexdigest()"
  val zeroTime = 1_600_101_725L // System.currentTimeMillis() / 997

  var userConfig = config.get[KingdeeConfig]("")

  def setConfig(dcId: String) = {
    this.userConfig = config.get[KingdeeConfig](dcId)
    this
  }

  def loginAuth(authUser: AuthUser): CookieDict = {
    val response_loginFalse = Json.parse("""{"Message":"用户名或密码错误！","MessageCode":"002005030013414","LoginResultType":0,"AccessToken":null,"KdAccessResult":null,"IsSuccessByAPI":false}""")
    val response_loginTrue = Json.obj("Message" -> None, "MessageCode" -> "CheckPasswordPolicy", "LoginResultType" -> 1, "AccessToken" -> None, "KdAccessResult" -> None, "IsSuccessByAPI" -> true)
    val response_noUser = Json.obj("Message" -> "用户名不存在！", "MessageCode" -> "002005030013414", "LoginResultType" -> 0, "AccessToken" -> None, "KdAccessResult" -> None, "IsSuccessByAPI" -> false)
    val key = authUser.acctID + "@" + authUser.userName
    val kv = cache.getOrElseUpdate[KingdeeConfig](key, 1.hours) {
      config.get[KingdeeConfig](authUser.acctID)
    }
    logger.debug(s"KingdeeConfig(${authUser.acctID}) = ${kv.dCID},${kv.userName}")
    if (authUser.userName == kv.userName && authUser.password == kv.pwd) {
      val aspkey = java.util.UUID.nameUUIDFromBytes(key.getBytes).toString
      val userCache = cache.getOrElseUpdate[AuthUser](aspkey, 10.minutes) {
        authUser
      }
      val kdSessionid = sec.hmacSHA256(userCache.acctID + userCache.userName, aspkey)
      CookieDict(aspSession = aspkey, kdSessionid = kdSessionid, messages = response_loginTrue)
    }
    else CookieDict(messages = response_noUser)
  }

  def validateUser(cookieDict: CookieDict): AuthUser = {
    logger.debug(s"validateUser:$cookieDict")
    val userCache = cache.get[AuthUser](cookieDict.aspSession).getOrElse(AuthUser("", "", ""))
    val kdSessionid = sec.hmacSHA256(userCache.acctID + userCache.userName, cookieDict.aspSession)
    if (kdSessionid.equals(cookieDict.kdSessionid)) userCache else AuthUser("", "", "")
  }

  def validateUser2(apiUrl:String,headers: Seq[(String, String)]): AuthUser = {

    var client_id: String = ""
    var timestamp: String = ""
    var api_sign: String = ""
    var app_data: String = ""
    var app_sign: String = ""

    // timestamp 1_665_992_598L
    //           1_600_101_725L
    val h = headers.iterator
    while (h.hasNext) {
      val i = h.next()
      logger.debug(s"webApi header => ${i._1}: ${i._2}")
      i._1 match {
        case "X-Api-ClientID" => client_id = i._2
        case "x-api-timestamp" => timestamp = i._2
        case "X-Api-Signature" => api_sign = i._2
        case "X-Kd-Appdata" => app_data = i._2
        case "X-Kd-Signature" => app_sign = i._2
        case _ =>
      }
    }
    if (app_data.isEmpty) {
      AuthUser()
    } else {
      val authUser = {
        val s = new String(security.Encode.base642Byte(app_data),Charset.forName("UTF-8"))
        val t = s.split(",")
         AuthUser(
          acctID = t(0),
          userName = t(1),
          password = t(3),
          lcid = t(2).toInt
        )
      }
      logger.debug(s"validateUser2:$authUser")
      val key = authUser.acctID + "@" + authUser.userName
      val kddc = cache.getOrElseUpdate[KingdeeConfig](key, 1.hours) {
        config.get[KingdeeConfig](authUser.acctID)
      }
      val url = if (kddc.serverUrl.endsWith("/")) kddc.serverUrl + apiUrl else kddc.serverUrl + "/" + apiUrl

      val headers1 = KingdeeAuthService.signature(url, timestamp, kddc.copy(dCID = authUser.acctID))
      logger.debug(s"$headers1")
      val nowtime = System.currentTimeMillis()
      val tc = if(timestamp.toLong< 1666944480000L){
        timestamp.toLong - nowtime/1000
      }else (timestamp.toLong - nowtime)/1000
      if( (tc*tc)< 90000 && headers1.Kd_Signature.equals(app_sign) && headers1.Api_Signature.equals(api_sign)){
        authUser
      }else{
        AuthUser()
      }
    }
  }
}

object KingdeeAuthService {
  private def decodeAppSecret(app_secret: String) = {
    if (app_secret.length != 32) {
      ""
    } else {
      val base64_decode = base642Byte(app_secret)
      val base64_xor = xor_code(base64_decode)
      byte2Base64(base64_xor)
    }
  }

  private def xor_code(bytes: Array[Byte]) = {
    val pwd_array = "0054f397c6234378b09ca7d3e5debce7".getBytes("UTF8")
    val code = mutable.ArrayBuffer[Byte]()
    for (i <- 0 until bytes.length) {
      val b = bytes(i) ^ pwd_array(i)
      code.append(b.toByte)
    }
    code.toArray
  }

  def signature(serverUrl: String, ts: String, kddc: KingdeeConfig) = {
    val path_url = if (serverUrl.startsWith("http")) {
      val p_index = serverUrl.indexOf("/", 10)
      serverUrl.substring(p_index)
    } else {
      serverUrl
    }
//    println(path_url)
    val nonce = ts
    val time_stamp = nonce
    val arr = kddc.appId.split("_")
    val cliend_id = arr(0)
    val cliend_sec = decodeAppSecret(arr(1))
    val api_sign = "POST\n" + path_url.replace("/", "%2F") + "\n\nx-api-nonce:" + nonce + "\nx-api-timestamp:" + time_stamp + "\n"
    val app_data = kddc.dCID + "," + kddc.userName + "," + kddc.lCID + "," + kddc.orgNum
    KingdeeAuthHeaders(
      cliend_id = cliend_id,
      Api_Signature = byte2Base64(hmacSHA256(api_sign, cliend_sec).getBytes()),
      Kd_Appkey = kddc.appId,
      Kd_Appdata = byte2Base64(app_data.getBytes("UTF8")),
      Kd_Signature = byte2Base64(hmacSHA256(kddc.appId + app_data, kddc.appSecret).getBytes()),
      timestamp = time_stamp,
      nonce = nonce
    )
  }
}

case class KingdeeAuthHeaders(
                               var cliend_id: String = "",
                               version: String = "2.0",
                               var timestamp: String = "",
                               var nonce: String = "",
                               api_signheaders: String = "x-api-timestamp,x-api-nonce",
                               var Api_Signature: String = "",
                               var Kd_Appkey: String = "",
                               var Kd_Appdata: String = "",
                               var Kd_Signature: String = "",
                               Accept_Charset: String = "utf-8",
                               User_Agent: String = "Kingdee/Python WebApi SDK 7.3 (compatible; MSIE 6.0; Windows NT 5.1;SV1)",
                               Content_Type: String = "application/json"
                             )
